Skip to content

guard negative operands in MathLib::value shift operators#8639

Open
metsw24-max wants to merge 2 commits into
cppcheck-opensource:mainfrom
metsw24-max:mathlib-value-shift-negative-guard
Open

guard negative operands in MathLib::value shift operators#8639
metsw24-max wants to merge 2 commits into
cppcheck-opensource:mainfrom
metsw24-max:mathlib-value-shift-negative-guard

Conversation

@metsw24-max

Copy link
Copy Markdown

the value shift operators only reject a count >= bigint_bits, so MathLib::value::shiftLeft still left-shifts a negative value and both shiftLeft and shiftRight still shift by a negative count, which is undefined behaviour. it is reachable when folding a template argument like 0x8000000000000000 << 1 in simplifyNumericCalculations, since MathLib::isNegative only checks for a leading minus while a large hex literal parses to a negative bigint and the guard there lets it through. mirror the negative-operand check calculate.h already uses and return the operand unchanged, as is already done for oversized counts. ubsan flags the left shift at mathlib.cpp:272 on that input.

@chrchr-github

chrchr-github commented Jun 26, 2026

Copy link
Copy Markdown
Collaborator

Thanks for your contribution.
This (invalid) code seems to trigger the bug:

template <uint64_t u>
uint64_t f() { return u; }
int main() {
    f<1 << 0x8000000000000000>();
}

Please add such a test case in testsimplifytemplate.cpp (even better if you have a valid example).

Regarding the CI failure, there might be missing CPPCHECKLIB annotations in mathlib.h for the freestanding operators and the nested type.

Move the regression test to testsimplifytemplate.cpp so it exercises the
reachable path through simplifyNumericCalculations instead of calling the
MathLib::value operators directly, which are not exported and broke the
Windows/test link.
@metsw24-max

Copy link
Copy Markdown
Author

Moved the regression test into testsimplifytemplate.cpp.

On the example: the bare f<1 << 0x8000000000000000>() doesn't actually reach the fold. The < of the argument list sits in front of the operand, so the associativity guard in simplifyNumericCalculations breaks the loop before shiftLeft is ever called and the expression is left untouched either way. Wrapping the shift in parentheses does reach it, so the test uses:

template <long long> struct S { };
S<(0x8000000000000000 << 1)> s1;   // negative left operand
S<(1 << 0x8000000000000000)> s2;   // negative count
S<(1 >> 0x8000000000000000)> s3;   // negative count, shiftRight

Without the guard s1 folds to S<0U> (the UB left shift); with it the operand is returned unchanged and the output stays S<9223372036854775808U>. That covers both new shiftLeft conditions and the shiftRight one.

On the CI failure: moving the test off the MathLib::value operators sidesteps it, since those and the nested type aren't exported from the DLL. This keeps it from adding CPPCHECKLIB annotations just for a unit test, though I can export them instead if you'd prefer a direct MathLib test.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants